/**
 * SimpleDiskCache.java  [V 1.0.0]
 * classpath : com.zyw.utils.SimpleDiskCache
 * zhangyw Create at 2014年8月11日 下午3:52:19
 */
package com.zyw.utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map.Entry;

import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.util.Log;

/**
 * 自行实现的diskcache，尝试采用linkedHashMap来实现Lru策略 由于使用的是这样简单的实现方式，导致程序的耦合性增加。我需要不断进行完善
 * 
 * @author zhangyw create at 2014年8月11日 下午3:52:19
 */
public class SimpleDiskCache {
  private static final Charset UTF_8 = Charset.forName("UTF-8");
  private static final int IO_BUFFER_SIZE = 8 * 1024;
  private static final String TAG = "SimpleDiskCache";

  // Compression settings when writing images to disk cache
  private static final CompressFormat DEFAULT_COMPRESS_FORMAT = CompressFormat.JPEG;
  private static final int DEFAULT_COMPRESS_QUALITY = 70;
  private static final int DISK_CACHE_INDEX = 0;
  private static final int DEFAULT_CAPACITY = 10;
  private static final float DEFAULT_LOAD_FACTOR = 0.75f;

  /**
   * 锁对象 这么写是因为据说初始化比object更快
   */
  private final byte[] lock = new byte[0];
  /**
   * 单位为kb
   */
  private long cacheSize;
  private File cacheDir;
  /**
   * key为检索项，vaule为存放路径
   */
  private final LinkedHashMap<String, String> mCacheMap;

  public SimpleDiskCache(File cacheParentDir, long size) {
    this(cacheParentDir, TAG, size);
  }

  /**
   * 初始化一个cache
   * 
   * @param cache的父文件夹位置
   * @param cache的名称
   * @param size指定缓存空间的大小
   */
  public SimpleDiskCache(File cacheParentDir, String cacheName, long size) {
    this.cacheSize = size;
    // cache会在创建一个自己的子目录
    cacheDir = new File(cacheParentDir, cacheName);
    if (cacheDir.isDirectory()) {
      File[] files = cacheDir.listFiles();
      mCacheMap = new LinkedHashMap<String, String>(
          (files.length > DEFAULT_CAPACITY) ? files.length : DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, true);
      for (File tmp : files) {
        mCacheMap.put(tmp.getName(), tmp.getAbsolutePath());
      }
    } else {
      cacheDir.mkdir();
      mCacheMap = new LinkedHashMap<String, String>(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, true);
    }
  }

  /**
   * @author zhangyw
   * @param bitmapKey
   * @return
   */
  public Bitmap readBitmapFromCache(String bitmapKey) {
    synchronized (lock) {
      String cachedFilePath = null;
      if (null != (cachedFilePath = mCacheMap.get(bitmapKey))) {
        return BitmapFactory.decodeFile(cachedFilePath);
      }
      return null;
    }
  }

  /**
   * @author zhangyw
   * @param bitmapKey
   * @param bitmap
   */
  public void addBitmap2Cache(String bitmapKey, Bitmap bitmap) {
    synchronized (lock) {
      // 先将bitmap压缩到文件
      File file2Cache = new File(cacheDir, bitmapKey);
      OutputStream out = null;
      try {
        try {
          out = new FileOutputStream(file2Cache);
          bitmap.compress(DEFAULT_COMPRESS_FORMAT, DEFAULT_COMPRESS_QUALITY, out);
        } finally {
          out.close();
        }
      } catch (Exception e) {
        if (e.getMessage() != null) {
          Log.w(TAG, e.getMessage());
        }
      }
      // 处理超过容量的情况
      long tmpSize = countUsedSpace();
      ArrayList<File> file2Del = new ArrayList<File>();
      while (isOverMaxSpace(tmpSize)) {
        Iterator<Entry<String, String>> iterator = mCacheMap.entrySet().iterator();
        Entry<String, String> oldestEntry = null;
        if (iterator.hasNext()) {
          oldestEntry = iterator.next();
        }
        if (oldestEntry != null) {
          File tmpFile = new File(oldestEntry.getValue());
          tmpSize -= tmpFile.length() / 1024;
          file2Del.add(tmpFile);
          mCacheMap.remove(oldestEntry.getKey());
        }
      }
      for (File tmp : file2Del) {
        Log.v(TAG, "delete the disk cache file " + tmp.getAbsolutePath());
        tmp.delete();
      }
      mCacheMap.put(bitmapKey, file2Cache.getAbsolutePath());
    }
  }

  /**
   * @author zhangyw
   * @return kb
   */
  public long countUsedSpace() {
    File[] fileList = cacheDir.listFiles();
    long rnt = 0;
    for (File tmp : fileList) {
      rnt += tmp.length() / 1024;
    }
    return rnt;
  }

  private boolean isOverMaxSpace(long currentSize) {
    if (currentSize > cacheSize) {
      return true;
    }
    return false;
  }

}
